package com.accesslimit.demo;

import java.time.LocalTime;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 固定窗口算法(计数器算法)
 *
 * @author sam
 */
public class RateLimiterSimpleWindow {

    // 阈值
    private static Integer QPS = 2;

    // 时间窗口
    private static long TIME_WINDOWS = 1000;

    // 计数器
    private static AtomicInteger REQ_COUNT = new AtomicInteger();

    // 开始时间
    private static long START_TIME = System.currentTimeMillis();

    /**
     * 测试主方法
     */
    public static void main(String[] args) throws InterruptedException {
        /***简易代码***/
//        Thread.sleep(400);
        for (int i = 0; i < 10; i++) {
            // 模拟每条请求间隔250ms
            Thread.sleep(250);
            // 获取当前时间
            LocalTime now = LocalTime.now();
            // 校验
            if (!tryAcquire()) {
                System.out.println(now + ": " + "该请求已被限流！");
            } else {
                System.out.println(now + ": " + "该请求已通过！");
            }
            /*
            09:58:08.690462200: 该请求已通过！
            09:58:08.963240900: 该请求已通过！
            09:58:09.213980: 该请求已被限流！
            09:58:09.464769500: 该请求已通过！
            09:58:09.715068200: 该请求已通过！
            09:58:09.965994100: 该请求已被限流！
            09:58:10.216971800: 该请求已被限流！
            09:58:10.467939300: 该请求已通过！
            09:58:10.718819400: 该请求已通过！
            09:58:10.969719200: 该请求已被限流！
             */
            /***问题存在***/
            /*
            假设第一个时间窗口 w1 = 1000ms
            假设第二个时间窗口 w2 = 1000ms
            - w1的后500ms和w2的前500ms,因为QPS=2,那这两个时间段内可以成功通过4个请求，不符合一秒限制两个请求的要求！
              -- 可以取消30行的注释，运行验证此问题是否存在！30行代码目的是为了更快地到达下一个新的时间窗口
             */
        }
    }

    /**
     * 校验是否限流
     *
     * @return boolean
     * @description 返回true代表不限流，false反之
     */
    private synchronized static boolean tryAcquire() {
        // 当前时间减去开始时间超过时间窗口，则计数器重置，开始时间重置
        if ((System.currentTimeMillis() - START_TIME) > TIME_WINDOWS) {
            REQ_COUNT.set(0);
            START_TIME = System.currentTimeMillis();
        }
        // 否则在时间窗口内，实施限流
        return REQ_COUNT.incrementAndGet() <= QPS;
    }

}
